﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Data.SqlClient;

namespace Lm.Common.DAL.MySql
{
    public class MySqlExpressionParser
    {
        protected MySqlParameterManager ParameterManager { get; set; }

        public MySqlExpressionParser(MySqlParameterManager parameterManager)
        { this.ParameterManager = parameterManager; }

        public string Parse(Expression fun)
        {
            switch (fun.NodeType)
            {
                case ExpressionType.Lambda:
                    {
                        var lmbd = fun as LambdaExpression;
                        return "(" + this.Parse(lmbd.Body) + ")";
                    }
                case ExpressionType.OrElse:
                    {
                        var oo = fun as BinaryExpression;
                        return " (" + this.Parse(oo.Left) + ") or (" + this.Parse(oo.Right) + ") ";
                    }
                case ExpressionType.GreaterThan:
                    {
                        var oo = fun as BinaryExpression;
                        return " (" + this.Parse(oo.Left) + ") > (" + this.Parse(oo.Right) + ") ";
                    }
                case ExpressionType.LessThan:
                    {
                        var oo = fun as BinaryExpression;
                        return " (" + this.Parse(oo.Left) + ") < (" + this.Parse(oo.Right) + ") ";
                    }
                case ExpressionType.Equal:
                    {
                        var oo = fun as BinaryExpression;
                        return " (" + this.Parse(oo.Left) + ") = (" + this.Parse(oo.Right) + ") ";
                    }
                case ExpressionType.NotEqual:
                    {
                        var oo = fun as BinaryExpression;
                        return " (" + this.Parse(oo.Left) + ") != (" + this.Parse(oo.Right) + ") ";
                    }
                case ExpressionType.GreaterThanOrEqual:
                    {
                        var oo = fun as BinaryExpression;
                        return " (" + this.Parse(oo.Left) + ") >= (" + this.Parse(oo.Right) + ") ";
                    }
                case ExpressionType.LessThanOrEqual:
                    {
                        var oo = fun as BinaryExpression;
                        return " (" + this.Parse(oo.Left) + ") <= (" + this.Parse(oo.Right) + ") ";
                    }
                case ExpressionType.MemberAccess:
                    {
                        var ma = fun as MemberExpression;
                        switch (ma.Expression.NodeType)
                        {
                            case ExpressionType.MemberAccess:
                                {
                                    List<string> keyList = new List<string>();
                                    var obj = this.GetValue(ma, keyList);
                                    var p = this.ParameterManager.Create(obj);
                                    return p.ParameterName;
                                }
                            case ExpressionType.Parameter:
                                {
                                    return "`" + ma.Member.Name + "`";
                                }
                            case ExpressionType.Constant:
                                {
                                    List<string> keyList = new List<string>();
                                    var obj = this.GetValue(ma, keyList);
                                    var p = this.ParameterManager.Create(obj);
                                    return p.ParameterName;
                                }
                            default:
                                {
                                    throw new NotImplementedException();
                                }
                        }
                    }
                case ExpressionType.Multiply:
                    {
                        var bin = fun as BinaryExpression;
                        return " (" + this.Parse(bin.Left) + ") + (" + this.Parse(bin.Right) + ") ";
                    }
                case ExpressionType.Constant:
                    {
                        var con = fun as ConstantExpression;
                        var p = this.ParameterManager.Create(con.Value);
                        return p.ParameterName;
                    }
                case ExpressionType.AndAlso:
                    {
                        var oo = fun as BinaryExpression;
                        return " (" + this.Parse(oo.Left) + ") and (" + this.Parse(oo.Right) + ") ";
                    }                
                case ExpressionType.Add:
                    {
                        var oo = fun as BinaryExpression;
                        return " (" + this.Parse(oo.Left) + ") + (" + this.Parse(oo.Right) + ") ";
                    }
                case ExpressionType.Call:
                    {
                        var call = fun as MethodCallExpression;
                        switch (call.Method.Name)
                        {
                            case "ContainsSqlIn":
                                {
                                    return ParseSqlIn(call, true);
                                }
                            case "NotContainsSqlNotIn":
                                {
                                    return ParseSqlIn(call, false);
                                }
                            case "StartWithSqlLike":
                                {
                                    return "(" + this.Parse(call.Arguments[0]) + ") like (( " + this.Parse(call.Arguments[1]) + " )+'%')";
                                }
                            case "NotStartWithSqlNotLike":
                                {
                                    return "(" + this.Parse(call.Arguments[0]) + ") not like (( " + this.Parse(call.Arguments[1]) + " )+'%')";
                                }
                            case "EndWithSqlLike":
                                {
                                    return "(" + this.Parse(call.Arguments[0]) + ") like ('%'+( " + this.Parse(call.Arguments[1]) + " ))";

                                }
                            case "NotEndWithSqlNotLike":
                                {
                                    return "(" + this.Parse(call.Arguments[0]) + ") not like ('%'+( " + this.Parse(call.Arguments[1]) + " ))";
                                }
                            case "ContainsSqlLike":
                                {
                                    return "(" + this.Parse(call.Arguments[0]) + ") like ('%'+ ( " + this.Parse(call.Arguments[1]) + " )+'%')";
                                }
                            case "NotContainsSqlNotLike":
                                {
                                    return "(" + this.Parse(call.Arguments[0]) + ") not like ('%'+ ( " + this.Parse(call.Arguments[1]) + " )+'%')";
                                }
                            case "IsNullSql":
                                {
                                    return " (" + this.Parse(call.Arguments[0]) + ") is null ";
                                }
                            case "IsNotNullSql":
                                {
                                    return " (" + this.Parse(call.Arguments[0]) + ") is not null ";
                                }
                            default:
                                { throw new Exception("错误的方法：" + call.Method.Name); }
                        }
                        throw new NotImplementedException();
                    }
                case ExpressionType.Convert:
                    {
                        var convert = fun as UnaryExpression;
                        return this.Parse(convert.Operand);
                    }
                default:
                    {
                        throw new Exception("没有处理的NodeType：" + fun.NodeType);
                    }
            }
        }

        protected string ParseSqlIn(MethodCallExpression call, bool isIn)
        {
            MemberExpression field;
            if (call.Arguments[1].NodeType == ExpressionType.MemberAccess)
            { field = call.Arguments[1] as MemberExpression; }
            else if (call.Arguments[1].NodeType == ExpressionType.Call
                &&
                ((MethodCallExpression)call.Arguments[1]).Method.Name == "ToString"
                )
            {
                field = ((MethodCallExpression)call.Arguments[1]).Object as MemberExpression;
            }
            else
            { throw new Exception("不支持的类型：" + call.Arguments[1].ToString()); }
            var values = call.Arguments[0] as MemberExpression;
            var con = values.Expression as ConstantExpression;
            var listObject = con.Value.GetType().GetFields().First().GetValue(con.Value);
            List<string> list = listObject as List<string>;
            if (list.Count == 0)
            { return " 1=0 "; }
            else
            {
                StringBuilder sbJoinValues = new StringBuilder();
                foreach (var i in list)
                {
                    sbJoinValues.Append("'" + i + "',");
                }
                return " " + this.Parse(field) + " " + (isIn ? "" : " not ") + " in (" + sbJoinValues.ToString().TrimEnd(',') + ")";
            }
        }

        protected object GetValue(Expression fun, List<string> keyList)
        {
            switch (fun.NodeType)
            {
                case ExpressionType.MemberAccess:
                    {
                        var ma = fun as MemberExpression;
                        keyList.Add(ma.Member.Name);
                        return this.GetValue(ma.Expression, keyList);
                    }
                case ExpressionType.Constant:
                    {
                        var con = fun as ConstantExpression;
                        if (con.Type.IsValueType)
                        { return con.Value; }
                        else
                        {
                            object obj = con.Value;
                            for (int i = keyList.Count - 1; i >= 0; i--)
                            {
                                var key = keyList[i];
                                if (obj.GetType().GetField(key) == null)
                                {
                                    obj = obj.GetType().GetProperty(key).GetValue(obj, null);
                                }
                                else
                                {
                                    obj = obj.GetType().GetField(key).GetValue(obj);
                                }
                            }
                            return obj;
                        }
                    }
                default:
                    { throw new NotImplementedException(); }
            }
        }
    }
}
